//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// C# Port port by: Lars Brubaker
//                  larsbrubaker@gmail.com
// Copyright (C) 2007
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using AGG.Image;
using AGG.Transform;
using AGG.VertexSource;

namespace AGG.UI
{
    public abstract class GUIWidget
    {
        private string name;
        private string text;

        private bool reflowLayoutAsNeeded = true;
		private bool mouseOverWidget = false;
        private bool visible = true;
        private rect_d bounds = new rect_d();
        private rect_d anchorOffsets = new rect_d();
        private GUIWidget parrent = null;
        private bool canFocus = true;
        private bool mouseCaptured;
        private bool isTabStop;
        private int tabIndex;

        [FlagsAttribute]
        public enum AnchorFlags { None = 0, Left = 1, Bottom = 2, Right = 4, Top = 8, All = Left | Bottom | Right | Top };

        private AnchorFlags anchorFlags = AnchorFlags.None;

        protected Transform.Affine transform = Affine.NewIdentity();
        public List<GUIWidget> m_Children = new List<GUIWidget>();

        private bool m_ContainsFocus = false;

        public delegate void MouseDownEventHandler(object sender, MouseEventArgs mouseEvent);
        public event MouseDownEventHandler MouseDown;

        public delegate void MouseUpEventHandler(object sender, MouseEventArgs mouseEvent);
        public event MouseUpEventHandler MouseUp;

        public delegate void MouseWheelEventHandler(object sender, MouseEventArgs mouseEvent);
        public event MouseWheelEventHandler MouseWheel;

        public delegate void MouseMoveEventHandler(object sender, MouseEventArgs mouseEvent);
        public event MouseMoveEventHandler MouseMove;

        public delegate void MouseEnterEventHandler(object sender, EventArgs e);
        public event MouseEnterEventHandler MouseEnter;

        public delegate void MouseLeaveEventHandler(object sender, EventArgs e);
        public event MouseLeaveEventHandler MouseLeave;

        public delegate void ResizeEventHandler(object sender, EventArgs e);
        public event ResizeEventHandler Resize;

        public GUIWidget()
        {
        }

        public virtual Vector2D Location
        {
            get 
            {
                Vector2D thisOrigin = new Vector2D(transform.tx, transform.ty);
                //Parrent.GetTransform().inverse_transform(ref thisOrigin);
                return thisOrigin; 
            }
            set 
            {
                transform.tx = value.x;
                transform.ty = value.y;
                //m_Transform.translate();
                //Bounds = new rect_d(value.x, value.y, value.x + Bounds.Width, value.y + Bounds.Height);
            }
        }

        /// <summary>
        /// <para>Cause the control to maintain it's specified edge(s) relative to it's parent's.</para>
        /// <para>If this needs to be scaled to mantain it's edges it will be.</para>
        /// 
        /// </summary>
        public AnchorFlags Anchor
        {
            get
            {
                return anchorFlags;
            }

            set
            {
                anchorFlags = value;
                if (Parrent == null)
                {
                    throw new NullReferenceException("You must have the parrent set before you can change the Anchor");
                }
                else
                {
                    anchorOffsets.Left = Bounds.Left - Parrent.Bounds.Left;
                    anchorOffsets.Bottom = Bounds.Bottom - Parrent.Bounds.Bottom;
                    anchorOffsets.Right = Parrent.Bounds.Right - Bounds.Right;
                    anchorOffsets.Top = Parrent.Bounds.Top - Bounds.Top;
                    UpdateLocationAndBounds();
                }
            }
        }

        /// <summary>
        /// <para>This sets the Anchor and position or bounds of this so that the specified edges will</para>
        /// <para>flush against the edges of it's parents.</para>
        /// </summary>
        /// <param name="anchorFlags"></param>
        public void DockToEdge(AnchorFlags anchorFlags)
        {
            Anchor = anchorFlags;
            anchorOffsets = new rect_d();
            if (Parrent == null)
            {
                throw new NullReferenceException("You must have the parrent set before you can Dock to an edge.");
            }
            else
            {
                UpdateLocationAndBounds();
            }
        }

        public virtual rect_d Bounds
        {
            get
            {
                return bounds;
            }
            set
            {
                if (value != bounds)
                {
                    bounds = value;
                    OnResize(new EventArgs());

                    // check the children for Anchor
                    foreach (GUIWidget child in m_Children)
                    {
                        if (child.Anchor != AnchorFlags.None)
                        {
                            child.UpdateLocationAndBounds();
                        }
                    }
                }
            }
        }

        protected virtual void UpdateLocationAndBounds()
        {
            if (anchorFlags != AnchorFlags.None)
            {
				Vector2D newLocation = Location;
                rect_d newBounds = Bounds;
                if ((Anchor & AnchorFlags.Right) == AnchorFlags.Right)
                {
                    if ((Anchor & AnchorFlags.Left) == AnchorFlags.Left)
                    {
                        // hold it to the left and widened the bounds
                        newLocation.x = Parrent.Bounds.Left + anchorOffsets.Left;
                        newBounds.Right = Parrent.Bounds.Right - anchorOffsets.Right;
                    }
                    else
                    {
                        // hold it to the right
                        newLocation.x = Bounds.Right + anchorOffsets.Right - Parrent.Bounds.Right;
                    }
                }
                else if ((Anchor & AnchorFlags.Left) == AnchorFlags.Left)
                {
                    // Hold it to the left
					newLocation.x = Parrent.Bounds.Left + anchorOffsets.Left - Bounds.Left;
                }

                if ((Anchor & AnchorFlags.Top) == AnchorFlags.Top)
                {
                    if ((Anchor & AnchorFlags.Bottom) == AnchorFlags.Bottom)
                    {
                        // bounds need to be streatched
                        newBounds.Bottom = Parrent.Bounds.Bottom + anchorOffsets.Bottom;
                        newBounds.Top = Parrent.Bounds.Top - anchorOffsets.Top;
                    }
                    else
                    {
                        // hold it to the Top
                        newBounds.Top = Parrent.Bounds.Top - anchorOffsets.Top;
                        newBounds.Bottom = newBounds.Top - Bounds.Height;
                    }
                }
                else if ((Anchor & AnchorFlags.Bottom) == AnchorFlags.Bottom)
                {
                    // hold it to the Bottom
                    newBounds.Bottom = Parrent.Bounds.Bottom + anchorOffsets.Bottom;
                    newBounds.Top = newBounds.Bottom + Bounds.Height;
                }

                Bounds = newBounds;
				Location = newLocation;
            }
        }

        public Vector2D Size
        {
            get
            {
                return new Vector2D(Bounds.Width, Bounds.Height);
            }

            set
            {
                Bounds = new rect_d(Bounds.Left, Bounds.Bottom, Bounds.Left + value.x, Bounds.Bottom + value.y);
            }
        }

        public string Name 
        {
            get
            {
                return name;
            }

            set
            {
                name = value;
            }
        }

        public int TabIndex
        {
            get
            {
                return tabIndex;
            }

            set
            {
                tabIndex = value;
            }
        }

        public bool TabStop
        {
            get
            {
                return isTabStop;
            }

            set
            {
                isTabStop = value;
            }
        }

        public virtual string Text
        {
            get
            {
                return text;
            }

            set
            {
                text = value;
            }
        }

        public void SetBounds(rect_i newBounds)
        {
            bounds.x1 = newBounds.x1;
            bounds.y1 = newBounds.y1;
            bounds.x2 = newBounds.x2;
            bounds.y2 = newBounds.y2;
        }

        public bool MouseCaptured
        {
            get { return mouseCaptured; }
            set { mouseCaptured = value; }
        }

        public bool Visible
        {
            get
            {
                return visible;

            }
            set
            {
                visible = value;
            }
        }

        public GUIWidget Parrent
        {
            get
            {
                return parrent;
            }
        }

        public double Height
        {
            get
            {
                return Bounds.Height;
            }
        }

        public double Width
        {
            get
            {
                return Bounds.Width;
            }
            set
            {
                Bounds = new rect_d(Bounds.Left, Bounds.Bottom, Bounds.Right + value, Bounds.Top);
            }
        }

        public void SuspendLayout()
        {
            reflowLayoutAsNeeded = false;
        }

        public void ResumeLayout()
        {
            ResumeLayout(true);
        }

        public void ResumeLayout(bool performLayout)
        {
            reflowLayoutAsNeeded = true;
            if (performLayout == true)
            {
                throw new NotImplementedException();
            }
        }

        public bool MouseOverWidget
		{
			get { return mouseOverWidget; }
		}

        public void AddChild(GUIWidget child)
        {
            child.parrent = this;
            m_Children.Add(child);
            child.OnParentChanged();
        }
        
        public void RemoveChild(GUIWidget child)
        {
            child.parrent = null;
            m_Children.Remove(child);
            child.OnParentChanged();
        }

        public virtual Graphics2D NewGraphics2D()
        {
            if (parrent != null)
            {
                Graphics2D widgetRenderer = parrent.NewGraphics2D();
                Affine transform = widgetRenderer.GetTransform();
                transform *= Transform;
                widgetRenderer.SetTransform(transform);

                rect_d currentScreenClipping = CurrentScreenClipping();
                widgetRenderer.SetClippingRect(currentScreenClipping);

                return widgetRenderer;
            }

            return null;
        }

        public virtual bool InRect(double x, double y)
        {
            if (Bounds.hit_test(x, y))
            {
                return true;
            }
            return false;
        }

        public virtual void Invalidate()
        {
            rect_d rectRelParent = Bounds;
            Transform.transform(ref rectRelParent);

            if (Parrent != null)
            {
                Parrent.Invalidate(rectRelParent);
            }
        }

        public virtual void Invalidate(rect_d rectToInvalidate)
        {
            if (Parrent != null)
            {
                Parrent.Invalidate(rectToInvalidate);
            }
        }

        protected void Unfocus()
        {
            m_ContainsFocus = false;
            foreach (GUIWidget child in m_Children)
            {
                child.Unfocus();
            }
        }

        public void Focus()
        {
            if(parrent != null)
            {
                parrent.Focus();
            }

            m_ContainsFocus = true;
        }

        public bool CanFocus
        {
            get
            {
                return canFocus;
            }

            set
            {
                canFocus = value;
            }
        }

        public bool Focused
        {
            get
            {
                if (ContainsFocus)
                {
                    foreach(GUIWidget child in m_Children)
                    {
                        if(child.ContainsFocus)
                        {
                            return false;
                        }
                    }

                    // we contain focus and none of our children do so we are focused.
                    return true;
                }

                return false;
            }
        }

        public bool ContainsFocus
        {
            get
            {
                return m_ContainsFocus;
            }
        }

        protected GUIWidget GetChildContainingFocus()
        {
            foreach(GUIWidget child in m_Children)
            {
                if(child.ContainsFocus)
                {
                    return child;
                }
            }

            return null;
        }

        public virtual void OnParentChanged()
        {
        }

        public virtual void OnDraw(Graphics2D graphics2D)
        {
            for(int i=0; i<m_Children.Count; i++)
            {
                GUIWidget child = m_Children[i];
                if(child.Visible)
                {
                    graphics2D.PushTransform();
                    Affine transform = graphics2D.GetTransform();
                    transform *= child.Transform;
                    graphics2D.SetTransform(transform);

                    rect_d currentScreenClipping = child.CurrentScreenClipping();
                    graphics2D.SetClippingRect(currentScreenClipping);
                    child.OnDraw(graphics2D);
					graphics2D.PopTransform();
                }
            }
			
#if false
            //graphics2D.SetClippingRect(new rect_d(0, 0, 1000, 1000));
			String temp = "P:" + Location.x.ToString() + ", " + Location.y.ToString();
			graphics2D.DrawString(temp, 0, 20);
			graphics2D.Line(-10, 0, 10, 0, RGBA_Bytes.Red);
			graphics2D.Line(0, -10, 0, 10, RGBA_Bytes.Green);
#endif

            //graphics2D.SetClippingRect(CurrentScreenClipping());
        }

        private rect_d CurrentScreenClipping()
        {
            rect_d screenClipping = Bounds;
            RectToScreen(ref screenClipping);

            if(Parrent != null)
            {
                rect_d screenParrentClipping = Parrent.CurrentScreenClipping();
                rect_d intersectionRect = new rect_d();
                intersectionRect.intersect_rectangles(screenClipping, screenParrentClipping);
                screenClipping = intersectionRect;
            }

            return screenClipping;
        }

        public virtual void OnClosed()
        {
            foreach (GUIWidget child in m_Children)
            {
                child.OnClosed();
            }
        }

        public void PointToClient(ref double screenPointX, ref double screenPointY)
        {
            GUIWidget curGUIWidget = this;
            while (curGUIWidget != null)
            {
                curGUIWidget.Transform.inverse_transform(ref screenPointX, ref screenPointY);
                curGUIWidget = curGUIWidget.Parrent;
            }
        }

        public void PointToClient(ref Vector2D screenPoint)
        {
            PointToClient(ref screenPoint.x, ref screenPoint.y);
        }

        public void PointToScreen(ref Vector2D clientPoint)
        {
            GUIWidget prevGUIWidget = this;
            while (prevGUIWidget != null && !prevGUIWidget.Transform.is_identity())
            {
                prevGUIWidget.Transform.transform(ref clientPoint);
                prevGUIWidget = prevGUIWidget.Parrent;
            }
        }

        public void RectToScreen(ref rect_d clientRect)
        {
            GUIWidget prevGUIWidget = this;
            while (prevGUIWidget != null)
            {
                prevGUIWidget.Transform.transform(ref clientRect);
                prevGUIWidget = prevGUIWidget.Parrent;
            }
        }

        public rect_d RectToScreen(rect_d clientRect)
        {
            rect_d screenRect = clientRect;
            GUIWidget prevGUIWidget = this;
            while (prevGUIWidget != null)
            {
                prevGUIWidget.Transform.transform(ref screenRect);
                prevGUIWidget = prevGUIWidget.Parrent;
            }

            return screenRect;
        }

        public virtual void OnMouseDown(MouseEventArgs mouseEvent)
        {
            if(Parrent == null)
            {
                Unfocus();
            }

            bool gaveAChildFocus = false;
            foreach (GUIWidget child in m_Children)
            {
                if (child.Visible)
                {
                    double newX = mouseEvent.X;
                    double newY = mouseEvent.Y;
                    child.Transform.inverse_transform(ref newX, ref newY);
                    MouseEventArgs childMouseEvent = new MouseEventArgs(mouseEvent, newX, newY);

                    if (child.InRect(childMouseEvent.X, childMouseEvent.Y))
                    {
                        if (child.CanFocus && !gaveAChildFocus)
                        {
                            child.MouseCaptured = true;
                            child.Focus();
                            gaveAChildFocus = true;
                        }

                        // recurse in
                        child.OnMouseDown(childMouseEvent);
                    }
                }
            }

            if (Parrent == null && m_ContainsFocus == false)
            {
                MouseCaptured = true;
                Focus();
            }

            if (MouseDown != null)
            {
                MouseDown(this, mouseEvent);
            }
        }

        public virtual void OnMouseMove(MouseEventArgs mouseEvent)
        {
            foreach (GUIWidget child in m_Children)
            {
                if (child.Visible)
                {
                    double newX = mouseEvent.X;
                    double newY = mouseEvent.Y;
                    child.Transform.inverse_transform(ref newX, ref newY);
                    MouseEventArgs childMouseEvent = new MouseEventArgs(mouseEvent, newX, newY);

                    child.OnMouseMove(childMouseEvent);
                }
            }

            bool doMouseLeaveEvent = false;

            if (InRect(mouseEvent.X, mouseEvent.Y))
            {
                if (!MouseOverWidget)
                {
                    mouseOverWidget = true;
                    if (MouseEnter != null)
                    {
                        MouseEnter(this, mouseEvent);
                    }
                }
            }
            else
            {
                if (MouseOverWidget)
                {
                    mouseOverWidget = false;
                    doMouseLeaveEvent = true;
                }
            }

            if (MouseMove != null)
            {
                MouseMove(this, mouseEvent);
            }

            if(doMouseLeaveEvent)
            {
                if (MouseLeave != null)
                {
                    MouseLeave(this, mouseEvent);
                }
            }
        }

        public virtual void OnMouseUp(MouseEventArgs mouseEvent)
        {
            foreach (GUIWidget child in m_Children)
            {
                if (child.Visible)
                {
                    double newX = mouseEvent.X;
                    double newY = mouseEvent.Y;
                    child.Transform.inverse_transform(ref newX, ref newY);
                    MouseEventArgs childMouseEvent = new MouseEventArgs(mouseEvent, newX, newY);

                    // recurse in
                    child.OnMouseUp(childMouseEvent);
                }
            }

            if (MouseUp != null)
            {
                MouseUp(this, mouseEvent);
            }

            if(MouseCaptured == true)
            {
                MouseCaptured = false;
            }
        }

        public virtual void OnMouseWheel(MouseEventArgs mouseEvent)
        {
            foreach (GUIWidget child in m_Children)
            {
                if (child.Visible)
                {
                    double newX = mouseEvent.X;
                    double newY = mouseEvent.Y;
                    child.Transform.inverse_transform(ref newX, ref newY);
                    MouseEventArgs childMouseEvent = new MouseEventArgs(mouseEvent, newX, newY);

                    // recurse in
                    child.OnMouseWheel(childMouseEvent);
                }
            }

            if (MouseWheel != null)
            {
                MouseWheel(this, mouseEvent);
            }
        }

        public virtual void OnKeyPress(KeyPressEventArgs keyPressEvent)
        {
            GUIWidget childWithFocus = GetChildContainingFocus();
            if (childWithFocus != null && childWithFocus.Visible)
            {
                childWithFocus.OnKeyPress(keyPressEvent);
            }
        }

        public virtual void OnResize(EventArgs e)
        {
            if (Resize != null)
            {
                Resize(this, e);
            }
        }

        protected void FocusNext()
        {
            GUIWidget childWithFocus = GetChildContainingFocus();
            for (int i = 0; i < m_Children.Count; i++)
            {
                GUIWidget child = m_Children[i];
                if(child.Visible)
                {
                }
            }
        }

        protected void FocusPrevious()
        {

        }

        public virtual void OnKeyDown(KeyEventArgs keyEvent)
        {
            GUIWidget childWithFocus = GetChildContainingFocus();
            if (childWithFocus != null && childWithFocus.Visible)
            {
                childWithFocus.OnKeyDown(keyEvent);
            }

            if (!keyEvent.Handled && keyEvent.KeyCode == Keys.Tab)
            {
                if (keyEvent.Shift)
                {
                    FocusPrevious();
                }
                else
                {
                    FocusNext();
                }
            }
        }

        public virtual void OnKeyUp(KeyEventArgs keyEvent)
        {
            GUIWidget childWithFocus = GetChildContainingFocus();
            if (childWithFocus != null && childWithFocus.Visible)
            {
                childWithFocus.OnKeyUp(keyEvent);
            }
        }

        public void SendToBack()
        {
            if (Parrent == null)
            {
                return;
            }

            Parrent.m_Children.Remove(this);
            Parrent.m_Children.Insert(0, this);
        }

        public bool SetChildCurrent(double x, double y)
        {
            for (int i = 0; i < m_Children.Count; i++)
            {
                GUIWidget child = m_Children[i];
                if (child.Visible)
                {
                    if (child.InRect(x, y))
                    {
                        if (!child.Focused)
                        {
                            child.Focus();
                            return true;
                        }

                        return false;
                    }
                }
            }

            return false;
        }

        public Affine Transform
        {
            get
            {
                return transform;
            }

            set
            {
                transform = value;
            }
        }

        public double scale() { return Transform.GetScale(); }
    };

    abstract public class SimpleVertexSourceWidget : GUIWidget, IVertexSource
    {
        public SimpleVertexSourceWidget(double x1, double y1, double x2, double y2)
        {
            Bounds = new rect_d(x1, y1, x2, y2);
        }

        public abstract int num_paths();
        public abstract void rewind(int path_id);
        public abstract Path.FlagsAndCommand vertex(out double x, out double y);

        public virtual IColorType color(int i) { return (IColorType) new RGBA_Doubles(); }

        public override void OnDraw(Graphics2D graphics2D)
        {
            for (int i = 0; i < num_paths(); i++)
            {
                graphics2D.Render(this, i, color(i).GetAsRGBA_Bytes());
            }
            base.OnDraw(graphics2D);
        }

#if false
        public override void Render(Renderer renderer)
        {
            for (int i = 0; i < num_paths(); i++)
            {
                renderer.Render(this, i, color(i).Get_rgba8());
            }
        }

        public void Render(IRasterizer rasterizer, IScanline scanline, IPixelFormat rendererBase)
        {
            int i;
            for(i = 0; i < num_paths(); i++)
            {
                rasterizer.reset();
                rasterizer.add_path(this, i);
                Renderer.RenderSolid(rendererBase, rasterizer, scanline, color(i).Get_rgba8());
            }
        }
#endif
    }
}
